/*********************************************************************************
 *      Copyright:  (C) 2020 makun<1394987689@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  mosquitto_pub.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(2020年07月08日)
 *         Author:  makun <1394987689@qq.com>
 *      ChangeLog:  1, Release initial version on "2020年07月08日 22时30分04秒"
 *                 
 ********************************************************************************/
#include "mosquitto_sub.h"


#define  PROG_VERSION "1.0.0"
#define  PATH_INT "./mqtt_aly_conf.ini"


static void  print_usage( char *progname)
{
    printf("Usage  %s [option]...\n", progname);
    printf("%s is makun studi MQTT daemon program running on RaspberryPi\n", progname);

    printf("-p (--port):   the port of the server you want to connect\n");
    printf("-h (--host):   the hostname of the server you want to connect\n");
    printf("-u (--user):   the username of the client\n");
    printf("-P (--passwd): the passwd of the client you\n");
    printf("-i (--clientid): the clientid of the user\n");
    printf("-t (--topic):  the topic of the client you want to pub\n");
    printf("-H (--help): Display this help information\n");
    printf("-v (--version): Display the program version\n");

    printf("%s  Version %s\n", progname, PROG_VERSION);
    return ;
}

int main (int argc, char **argv)
{
    char         *host = NULL;
    int          port ;
    char         *clientid = NULL;
    char         *user = NULL;
    char         *passwd = NULL;
    char         *topic = NULL;
    int          rv = -1;
    int          opt= -1;
    char         *progname = NULL;
    bool         session = true;
    mqtt_ctx_t   mqtt;

    struct mosquitto *mosq = NULL;

    
    struct option long_options[]= {
        {"host", required_argument, NULL, 'h'},
        {"port", required_argument, NULL, 'p'},
        {"user", required_argument, NULL, 'u'},
        {"passwd",required_argument, NULL,'P'},
        {"topic", required_argument, NULL, 't'},
        {"clientid", required_argument, NULL, 'i'},
        {"help", no_argument, NULL, 'H'},
        {"version", no_argument, NULL, 'v'},
        {0, 0, 0, 0}
    };

    progname = (char *)basename(argv[0]);

    while( (opt = getopt_long(argc, argv,"h:p:u:P:i:tHv", long_options,NULL)) != -1)
    {
        switch (opt)
        {
            case 'h':
                host = optarg;
                break;
            case 'p':
                port = atoi(optarg);
                break;
            case 'u':
                user = optarg;
                break;
            case 'P':
                passwd = optarg;
                break;
            case 'i':
                clientid = optarg;
            case 't':
                topic = optarg;
                break;
            case 'v':
                printf("%s Version %s\n",progname, PROG_VERSION);
                return 0;
            case 'H':
                print_usage(progname);
                return 0;
            default:
                break;
        }
    }

    rv=set_mqtt_conf(PATH_INT, host, port, clientid, user,passwd, topic);
    if(rv < 0)
    {
        printf("set mqtt conf is failure %d\n", rv);
        return -1;
    }

    memset(&mqtt, 0, sizeof(mqtt));

    rv = gain_mqtt_conf(PATH_INT, &mqtt);
    if(rv < 0)
    {
        printf("gain mqtt conf failure %d\n", rv);
        return -2;
    }

    /*必须在任何其他mosquitto功能之前调用*/
    mosquitto_lib_init();

    /*创建一个新的mosquitto客户端实例,第二个参数为true，代理清除断开连接时的所有消息和订阅*/
    mosq = mosquitto_new(mqtt.clientid,session, (void *)&mqtt );
    if(!mosq)
    {
        printf("mosquitto new failure: %s\n", strerror(errno));
        goto cleanup;
    }

    printf("Create mosquitto successfuly\n");

    /*设置连接回调，当代理发送CONNACK消息以响应连接时，将调用此方法*/
    mosquitto_connect_callback_set(mosq, sub_conn_callback);

    /*消息回调函数*/
    mosquitto_message_callback_set(mosq, message_callback);


    /*配置mosquitto实例的用户名和密码*/
    if( mosquitto_username_pw_set(mosq, mqtt.username,mqtt.passwd) !=MOSQ_ERR_SUCCESS)
    {
        printf("mosquitto username and passwd failure:%s\n",strerror(errno));
        goto cleanup;
    }

    while(1)
    {
        /*连接MQTT代理*/
        if(mosquitto_connect(mosq, mqtt.hostname, mqtt.port, mqtt.keepalive) != MOSQ_ERR_SUCCESS )
        {
            printf("mosquitto connect server failure:%s\n",strerror(errno));
            continue;
            sleep(1);
        }

        /*无线阻塞循环调用loop*/
        mosquitto_loop_forever(mosq, -1, 1 );
        sleep(10);
    }

cleanup:
    printf("program will exit\n");
    mosquitto_destroy(mosq);
    mosquitto_lib_cleanup();

    return 0;

}


void sub_conn_callback(struct mosquitto *mosq, void *obj, int rc)
{
    mqtt_ctx_t  *mqtt;
    int         mid;
    

    if(!mosq ||!obj)
    {
        printf("invalid input argument\n");
        return ;
    }

    mqtt = (mqtt_ctx_t *)obj;


    if(!rc)
    {  
        if( mosquitto_subscribe(mosq,&mid,mqtt->subTopic, 0) != MOSQ_ERR_SUCCESS )  
        {   
            printf("Mosq subscribe() error: %s\n", strerror(errno));
            return ;
        }
        printf("subscribe topic:%s\n",mqtt->subTopic) ;
    }
}

void message_callback(struct mosquitto *mosg, void *userdata,const struct mosquitto_message *message)
{
    mqtt_ctx_t  *mqtt;
    cJSON  *payload_json = cJSON_CreateObject();
    char    value[256];

    if(!mosg || !userdata)
    {
        printf("Invalid input argument!\n");
        return ;
    }
    mqtt = (mqtt_ctx_t *)userdata;
    if(message->payloadlen)
    {
        payload_json = cJSON_Parse(message->payload);
#if 0
        printJson(payload_josn);
#endif
        printf("%s\n",cJSON_Print(payload_json));
        find_identi_Json(payload_json,mqtt->identifier, value, sizeof(value) ) ;
        printf("\n\"%s\":\"%s\"\n", mqtt->identifier, value);
    }
}

void printJson(cJSON * root)//以递归的方式打印json的最内层键值对
{
    int             i ;
    for(i=0; i<cJSON_GetArraySize(root); i++)   //遍历最外层json键值对
    {
        cJSON * item = cJSON_GetArrayItem(root, i);
        if(cJSON_Object == item->type)      //如果对应键的值仍为cJSON_Object就递归调用printJson
        printJson(item);
        else                                //值不为json对象就直接打印出键和值
        {
            printf("%s->", item->string);
            printf("%s\n", cJSON_Print(item));
        }
    }
}
void find_identi_Json(cJSON * root,const char* identifier, char *reval,int reval_size) //以递归的方式寻找对应的标识符对应的值
{
    int             i ;
    cJSON * item = cJSON_GetArrayItem(root, i); 
    if(cJSON_Object == item->type)      //如果对应键的值仍为cJSON_Object就递归查找
        find_identi_Json(item, identifier, reval ,reval_size);
    
    else
    {
        if(!strcasecmp(item->string, identifier))
            strncpy(reval,cJSON_Print(item), reval_size);

    }
}




